HAS_HELM_UNITTEST := $(shell helm plugin list | grep unittest 2> /dev/null)
HAS_SHELLSPEC := $(shell command -v shellspec;)

CHART_TEMPLATE_FILES = $(shell find templates -name "*.tpl")
CHART_YAML_FILES = $(shell find templates -name "*.yaml")

FEATURE_CHARTS = $(shell find charts -name Chart.yaml -depth 2 -exec dirname {} \;)

SCHEMA_MODS_JSON_FILES = $(shell find schema-mods -name "*.json")
SCHEMA_MODS_JQ_FILES = $(shell find schema-mods -name "*.jq")

ALLOY_OPERATOR_VERSION := $(shell yq '.dependencies[] | select(.name == "alloy-operator") | .version' Chart.yaml)
collectors/upstream/alloy-values.yaml: charts/alloy-operator-$(ALLOY_OPERATOR_VERSION).tgz
	tar xzf $< -C collectors/upstream/ --strip-components=1 alloy-operator/alloy-values.yaml

COLLECTOR_VALUES_FILES = $(shell find collectors -maxdepth 1 -name "*-values.yaml" | sort -u)
COLLECTOR_DOCS_FILES = $(COLLECTOR_VALUES_FILES:collectors/%-values.yaml=./docs/collectors/%.md)
COLLECTOR_SCHEMA_FILES = $(COLLECTOR_VALUES_FILES:collectors/%-values.yaml=./schema-mods/definitions/%-collector.schema.json)

DESTINATION_VALUES_FILES = $(shell find destinations -name "*-values.yaml" | sort -u)
DESTINATION_DOCS_FILES = $(DESTINATION_VALUES_FILES:destinations/%-values.yaml=./docs/destinations/%.md)
DESTINATION_SCHEMA_FILES = $(DESTINATION_VALUES_FILES:destinations/%-values.yaml=./schema-mods/definitions/%-destination.schema.json)

INTEGRATION_VALUES_FILES = $(shell find charts/feature-integrations/integrations -name "*-values.yaml" 2>/dev/null | sort -u)

.SECONDEXPANSION:
docs/collectors/%.md: collectors/%-values.yaml $$(wildcard docs/collectors/.doc_templates/%.gotmpl)
	docker run --platform linux/amd64 --rm --volume $(shell pwd):/chart ghcr.io/grafana/helm-chart-toolbox-doc-generator --file /chart/$< --template /chart/docs/collectors/.doc_templates/$(shell basename -s -values.yaml $<).gotmpl > $@

docs/destinations/%.md: destinations/%-values.yaml $$(wildcard docs/destinations/.doc_templates/%.gotmpl)
	docker run --platform linux/amd64 --rm --volume $(shell pwd):/chart ghcr.io/grafana/helm-chart-toolbox-doc-generator --file /chart/$< --template /chart/docs/destinations/.doc_templates/$(shell basename -s -values.yaml $<).gotmpl > $@

schema-mods/definitions/%-collector.schema.json: collectors/%-values.yaml
	docker run --platform linux/amd64 --rm --volume $(shell pwd):/chart ghcr.io/grafana/helm-chart-toolbox-schema-generator --file /chart/$< > $@

schema-mods/definitions/%-destination.schema.json: destinations/%-values.yaml
	docker run --platform linux/amd64 --rm --volume $(shell pwd):/chart ghcr.io/grafana/helm-chart-toolbox-schema-generator --file /chart/$< > $@

NUMBER_OF_DESTINATION_VALUES_FILES := $(words $(DESTINATION_VALUES_FILES))
schema-mods/destination.json: $(DESTINATION_SCHEMA_FILES)
	@echo '{' > $@
	@echo '  "definitions": {' >> $@
	@echo '    "destination": {' >> $@
	@echo '      "type": "object",' >> $@
	@echo '      "anyOf": [' >> $@
	@echo '        { "$$ref": "#/definitions/invalid-destination"},' >> $@
	@count=0; \
	for file in $(DESTINATION_VALUES_FILES); do \
		count=$$((count + 1)); \
		if [ $$count -eq $(NUMBER_OF_DESTINATION_VALUES_FILES) ]; then \
			echo '        { "$$ref": "#/definitions/'$$(echo $${file} | sed 's/destinations\/\([-a-z]*\)-values.yaml/\1/')'-destination"}' >> $@; \
		else \
			echo '        { "$$ref": "#/definitions/'$$(echo $${file} | sed 's/destinations\/\([-a-z]*\)-values.yaml/\1/')'-destination"},' >> $@; \
		fi; \
	done
	@echo '      ]' >> $@
	@echo '    },' >> $@
	@echo '    "destination-list": {' >> $@
	@echo '      "type": "array",' >> $@
	@echo '      "items": {' >> $@
	@echo '        "$$ref": "#/definitions/destination"' >> $@
	@echo '      }' >> $@
	@echo '    },' >> $@
	@echo '    "destination-map": {' >> $@
	@echo '      "type": "object",' >> $@
	@echo '      "additionalProperties": {' >> $@
	@echo '        "$$ref": "#/definitions/destination"' >> $@
	@echo '      }' >> $@
	@echo '    },' >> $@
	@count=0; \
	for file in $(DESTINATION_VALUES_FILES); do \
		count=$$((count + 1)); \
		name=$$(echo $${file} | sed 's/destinations\/\([-a-z]*\)-values.yaml/\1/'); \
		if [ $$count -eq $(NUMBER_OF_DESTINATION_VALUES_FILES) ]; then \
			echo '    "'$${name}'-destination": {"properties": {"type": {"type": "string", "const": "'$${name}'"}}}' >> $@; \
		else \
			echo '    "'$${name}'-destination": {"properties": {"type": {"type": "string", "const": "'$${name}'"}}},' >> $@; \
		fi; \
	done
	@echo '  }' >> $@
	@echo '}' >> $@

templates/destinations/_destination_types.tpl: $(DESTINATION_VALUES_FILES)
	echo '{{/* Do not edit this file. It is generated by the Makefile */}}' > $@
	echo '{{- define "destinations.types" }}' >> $@
	for file in $(DESTINATION_VALUES_FILES); do \
		echo - $$(echo $${file} | sed 's/destinations\/\([-a-z]*\)-values.yaml/\1/') >> $@; \
	done
	echo '{{- end -}}' >> $@

Chart.lock: Chart.yaml
	helm dependency update .
	@touch Chart.lock # Ensure the timestamp is updated

values.schema.json: values.yaml $(COLLECTOR_SCHEMA_FILES) $(DESTINATION_SCHEMA_FILES) $(SCHEMA_MODS_JSON_FILES) $(SCHEMA_MODS_JQ_FILES) schema-mods/destination.json
	docker run --rm --platform linux/amd64 --volume $(shell pwd):/chart ghcr.io/grafana/helm-chart-toolbox-schema-generator --chart /chart > $@

README.md: README.md.gotmpl values.yaml Chart.yaml
	docker run --rm --platform linux/amd64 --volume $(shell pwd):/chart ghcr.io/grafana/helm-chart-toolbox-doc-generator --chart /chart > $@

# AGENTS.md generation - auto-discovers features, destinations, and integrations
AGENTS.md: AGENTS.md.gotmpl $(FEATURE_CHARTS) $(DESTINATION_VALUES_FILES) $(INTEGRATION_VALUES_FILES)
	@cp AGENTS.md.gotmpl $@.tmp
	@# Generate features list
	@FEATURES_LIST=""; \
	for chart in $(FEATURE_CHARTS); do \
		name=$$(basename $$chart); \
		desc=$$(yq '.description // "No description"' $$chart/Chart.yaml 2>/dev/null || echo "No description"); \
		FEATURES_LIST="$$FEATURES_LIST\n-   \`$$name\` - $$desc"; \
	done; \
	awk -v features="$$FEATURES_LIST" '{ \
		if (/<!-- BEGIN FEATURES -->/) { print; printf "%s\n", features; skip=1 } \
		else if (/<!-- END FEATURES -->/) { skip=0; print } \
		else if (!skip) print \
	}' $@.tmp > $@.tmp2 && mv $@.tmp2 $@.tmp
	@# Generate destinations list
	@DEST_LIST=""; \
	for file in $(DESTINATION_VALUES_FILES); do \
		name=$$(basename $$file -values.yaml); \
		case $$name in \
			prometheus) desc="Remote write to Prometheus-compatible endpoint";; \
			loki) desc="Send logs to Loki";; \
			loki-stdout) desc="Send logs to stdout (debugging)";; \
			otlp) desc="Send to any OTLP endpoint (Tempo, Grafana Cloud, etc.)";; \
			pyroscope) desc="Send profiles to Pyroscope";; \
			custom) desc="Custom destination configuration";; \
			*) desc="Send data to $$name";; \
		esac; \
		DEST_LIST="$$DEST_LIST\n-   \`$$name\` - $$desc"; \
	done; \
	awk -v destinations="$$DEST_LIST" '{ \
		if (/<!-- BEGIN DESTINATIONS -->/) { print; printf "%s\n", destinations; skip=1 } \
		else if (/<!-- END DESTINATIONS -->/) { skip=0; print } \
		else if (!skip) print \
	}' $@.tmp > $@.tmp2 && mv $@.tmp2 $@.tmp
	@# Generate integrations list
	@INT_LIST=""; \
	for file in $(INTEGRATION_VALUES_FILES); do \
		name=$$(basename $$file -values.yaml); \
		INT_LIST="$$INT_LIST\n-   \`$$name\` - Monitor $$name"; \
	done; \
	awk -v integrations="$$INT_LIST" '{ \
		if (/<!-- BEGIN INTEGRATIONS -->/) { print; printf "%s\n", integrations; skip=1 } \
		else if (/<!-- END INTEGRATIONS -->/) { skip=0; print } \
		else if (!skip) print \
	}' $@.tmp > $@.tmp2 && mv $@.tmp2 $@.tmp
	@mv $@.tmp $@

# Example targets
EXAMPLE_RELEASE_NAME=k8smon
EXAMPLE_VALUES_FILES := $(shell find docs/examples -name values.yaml)
EXAMPLE_OUTPUT_FILES := $(EXAMPLE_VALUES_FILES:values.yaml=output.yaml)
EXAMPLE_ALLOY_FILES = $(foreach file,$(EXAMPLE_VALUES_FILES),$(call alloy_configs, $(file)))
EXAMPLE_README_FILES := $(EXAMPLE_VALUES_FILES:values.yaml=README.md)

NON_RENDERED_INTEGRATION_TEST_VALUES_FILES = $(shell find tests/integration -name .no-render | sed 's/.no-render/values.yaml/')
INTEGRATION_TEST_VALUES_FILES = $(filter-out $(NON_RENDERED_INTEGRATION_TEST_VALUES_FILES),$(shell find tests/integration -name values.yaml))
INTEGRATION_TEST_OUTPUT_FILES = $(INTEGRATION_TEST_VALUES_FILES:values.yaml=.rendered/output.yaml)
INTEGRATION_TEST_ALLOY_INSTANCE_FILES = $(foreach file,$(INTEGRATION_TEST_OUTPUT_FILES),$(call alloy_instances, $(file)))

NON_RENDERED_PLATFORM_TEST_VALUES_FILES := $(shell find tests/platform -name .no-render | sed 's/.no-render/values.yaml/')
PLATFORM_TEST_VALUES_FILES := $(filter-out $(NON_RENDERED_PLATFORM_TEST_VALUES_FILES),$(shell find tests/platform -name values.yaml))
PLATFORM_TEST_OUTPUT_FILES := $(PLATFORM_TEST_VALUES_FILES:values.yaml=.rendered/output.yaml)

alloy_configs = $(shell \
	DIR="$(shell dirname $(1))/" yq e 'with_entries(select(.key == "alloy-*" and .value.enabled == true)) | to_entries | env(DIR) + .[].key + ".alloy"' $(1) \
)
alloy_instances = $(shell \
	DIR="$(shell dirname $(1))/" yq e 'with_entries(select(.key == "alloy-*" and .value.enabled == true)) | to_entries | env(DIR) + .[].key + ".yaml"' $(1) \
)

%/output.yaml: %/values.yaml Chart.yaml Chart.lock values.yaml values.schema.json templates/destinations/_destination_types.tpl $(DESTINATION_VALUES_FILES) $(CHART_TEMPLATE_FILES) $(CHART_YAML_FILES)
	helm template $(EXAMPLE_RELEASE_NAME) . -f $< > $@

%/.rendered/output.yaml: %/values.yaml Chart.yaml Chart.lock values.yaml values.schema.json templates/destinations/_destination_types.tpl $(DESTINATION_VALUES_FILES) $(CHART_TEMPLATE_FILES) $(CHART_YAML_FILES)
	mkdir -p $(dir $@)
	helm template $(EXAMPLE_RELEASE_NAME) . -f $< > $@

ALLOY_INSTANCES := logs metrics receiver singleton profiles
define alloy_files
%/alloy-$(1).alloy: %/output.yaml
	yq 'select(.kind=="ConfigMap" and .metadata.name=="$(EXAMPLE_RELEASE_NAME)-alloy-$(1)") | .data["config.alloy"]' $$< > $$@
%/alloy-$(1).yaml: %/output.yaml
	yq 'select(.kind=="Alloy" and .metadata.name=="$(EXAMPLE_RELEASE_NAME)-alloy-$(1)") | .' $$< > $$@
endef
$(foreach instance,$(ALLOY_INSTANCES),$(eval $(call alloy_files,$(instance))))

%/README.md: %/values.yaml $$(wildcard %/description.txt)
	@echo '<!--' > $@
	@echo '(NOTE: Do not edit README.md directly. It is a generated file!)' >> $@
	@echo '(      To make changes, please modify values.yaml or description.txt and run `make examples`)' >> $@
	@echo '-->' >> $@

	@if [ -f $$(dirname $@)/description.txt ]; then \
		cat $$(dirname $@)/description.txt >> $@; \
	else \
		echo $< | sed -e "s/docs\/examples\//# Example: /" >> $@; \
	fi
	@echo '' >> $@
	@echo '## Values' >> $@
	@echo '' >> $@
	@echo '<!-- textlint-disable terminology -->' >> $@
	@echo '```yaml' >> $@
	@cat $< >> $@
	@echo '```' >> $@
	@echo '<!-- textlint-enable terminology -->' >> $@

.PHONY: examples
examples: $(EXAMPLE_OUTPUT_FILES) $(EXAMPLE_ALLOY_FILES) $(EXAMPLE_README_FILES) $(INTEGRATION_TEST_OUTPUT_FILES) $(PLATFORM_TEST_OUTPUT_FILES)

.PHONY: example-checks
example-checks: $(EXAMPLE_OUTPUT_FILES)
ifdef HAS_SHELLSPEC
	shellspec -c tests/example-checks
else
	docker run --platform linux/amd64 --rm --volume $(shell pwd):/src shellspec/shellspec -c /src/tests/example-checks -s /bin/sh
endif


.PHONY: misc-checks
misc-checks: Chart.yaml
ifdef HAS_SHELLSPEC
	ALLOY_OPERATOR_VERSION=$(ALLOY_OPERATOR_VERSION) shellspec -c tests/misc-checks
else
	docker run --platform linux/amd64 --rm --env ALLOY_OPERATOR_VERSION=$(ALLOY_OPERATOR_VERSION) --volume $(shell pwd):/src shellspec/shellspec -c /src/tests/misc-checks -s /bin/sh
endif

.PHONY: integration-test-checks
integration-test-checks: $(INTEGRATION_TEST_OUTPUT_FILES)
.PHONY: platform-test-checks
platform-test-checks: $(PLATFORM_TEST_OUTPUT_FILES)

.PHONY: clean
clean: clean-examples
	rm -f README.md AGENTS.md values.schema.json templates/destinations/_destination_types.tpl schema-mods/destination.json
	rm -f collectors/upstream/alloy-values.yaml $(COLLECTOR_SCHEMA_FILES) $(COLLECTOR_DOCS_FILES)
	rm -f $(DESTINATION_SCHEMA_FILES) $(DESTINATION_DOCS_FILES)
	set -e && \
	for chart in $(FEATURE_CHARTS); do \
		make -C $$chart clean; \
	done

.PHONY: clean-examples
clean-examples:
	rm -f $(EXAMPLE_OUTPUT_FILES) $(EXAMPLE_ALLOY_FILES) $(EXAMPLE_README_FILES) $(INTEGRATION_TEST_OUTPUT_FILES) $(PLATFORM_TEST_OUTPUT_FILES)

build-features:
	set -e && \
	for chart in $(FEATURE_CHARTS); do \
		make -C $$chart build; \
	done

# Build targets
.PHONY: build
build: build-features README.md AGENTS.md values.schema.json templates/destinations/_destination_types.tpl collectors/upstream/alloy-values.yaml $(COLLECTOR_DOCS_FILES) $(DESTINATION_DOCS_FILES) examples integration-test-checks platform-test-checks

# Test targets
.PHONY: test unittest lint-helm lint-configs
test: unittest lint-helm lint-configs example-checks misc-checks test-features

test-features:
	set -e && \
	for chart in $(FEATURE_CHARTS); do \
		make -C $$chart test; \
	done

lint-configs: $(EXAMPLE_ALLOY_FILES)
	../../scripts/lint-alloy.sh $(EXAMPLE_ALLOY_FILES)
	rm -rf data-alloy  # Clean up the data-alloy directory, which is created by lint-alloy.sh when it runs Alloy.

lint-helm: build
	helm repo add grafana https://grafana.github.io/helm-charts
	helm lint .
	ct lint --config .ct.yaml --lint-conf .lintconf.yaml --check-version-increment=false --charts .

unittest: values.schema.json templates/destinations/_destination_types.tpl
ifdef HAS_HELM_UNITTEST
	helm unittest --failfast --with-subchart=false .
else
	docker run --rm --volume $(shell pwd):/apps helmunittest/helm-unittest:3.18.4-1.0.0 --failfast --with-subchart=false .
endif

.PHONY: update-test-snapshots
update-test-snapshots:
ifdef HAS_HELM_UNITTEST
	helm unittest --with-subchart=false . --update-snapshot
else
	docker run --rm --volume $(shell pwd):/apps helmunittest/helm-unittest:3.18.4-1.0.0 --with-subchart=false . --update-snapshot
endif
	set -e && \
	for chart in $(FEATURE_CHARTS); do \
		make -C $$chart update-test-snapshots; \
	done
